package me.fung.wubi.base.define.table

import me.fung.wubi.base.define.action.ProcessAction
import me.fung.wubi.base.define.action.runInBackground
import me.fung.wubi.base.define.value.Code
import me.fung.wubi.base.define.value.Word
import me.fung.wubi.base.define.value.toCode
import me.fung.wubi.base.define.value.toWord
import me.fung.wubi.base.io.autoReadLines
import java.io.File

class CodeTable(
    mutableMap: MutableMap<Code, CodeItem> = HashMap()
) : MutableMap<Code, CodeItem> by mutableMap {

    val wordSize: Int
        get() {
            return map {
                it.value.size
            }.sum()
        }

    fun codeItems(): List<CodeItem> {
        return map { it.value }
    }

    fun itemOrNull(code: Code): CodeItem? {
        return this[code]
    }

    fun codeItemOf(code: Code): CodeItem {
        return get(code) ?: CodeItem(code).also { put(code, it) }
    }

    fun codeItemOrNull(code: Code): CodeItem? {
        return get(code)
    }

    fun add(code: Code, word: Word) {
        codeItemOf(code).add(word)
    }

    fun addCodeItem(codeItem: CodeItem) {
        val item = codeItemOf(codeItem.code)
        item.addCodeItem(codeItem)
    }

    fun remove(code: Code, word: Word) {
        codeItemOf(code).remove(word)
    }

    fun remove(codeItem: CodeItem) {
        remove(codeItem.code)
    }

    fun wordSize(): Int {
        return this.values.sumOf {
            it.wordSize()
        }
    }

    /**
     * 移除该[word]的所有词条，包含简码
     */
    fun removeAll(fullCode: Code, word: Word) {
        fullCode.subCodes().forEach { remove(it, word) }
    }

    fun addTable(source: CodeTable) {
        source.forEach { _, item ->
            addCodeItem(item)
        }
    }

    fun addTableAutoShort(source: CodeTable) {
        listOf(
            source.filter { (code, _) -> code.length == 1 },
            source.filter { (code, _) -> code.length == 2 },
            source.filter { (code, _) -> code.length == 3 },
            source.filter { (code, _) -> code.length == 4 },
        ).forEach { map ->
            map.forEach { (code, item) ->
                item.forEach { word ->
                    addAutoShort(code, word)
                }
            }
        }
    }

    fun addAutoShort(code: Code, word: Word) {
        val shortCodes = code.shortCodes(true)
        for (c in shortCodes) {
            val ci = codeItemOf(c)
            if (ci.isEmpty() || (c == code)) {
                add(c, word)
                break
            }
        }

    }

    fun addTables(sources: Collection<CodeTable>) {
        sources.forEach(::addTable)
    }

    fun replaceAll(item: CodeItem) {
        codeItemOf(item.code).replaceAll(item)
    }

    fun addAutoShort(lexicon: Lexicon, enableAutoShort: Boolean = false) {
        val list = ArrayList<Pair<Word, Code>>()
        for (len in 1..4) {
            lexicon.filter { it.value.length == len }.map(Map.Entry<Word, Code>::toPair).forEach(list::add)
        }
        list.forEach { (word, code) ->
            var c = code
            if (enableAutoShort) {
                val sc = code.shortCode()
                // 编码词条为空时才允许自动简码
                val toShort = sc?.let {
                    codeItemOf(it).isEmpty()
                } ?: false
                if (toShort) {
                    c = sc!!
                }
            }
            add(c, word)
        }

    }

    /**
     * line: code word word
     */
    fun loadFromFile(file: File, action: ProcessAction? = null): CodeTable {
        file.autoReadLines().forEach FE@{
            if (it.startsWith("#")) return@FE
            val list = it.split(' ', '\t')
            if (list.size >= 2) {
                val code = list[0].toCode()
                for (i in 1 until list.size) {
                    if (list[i].isEmpty()) break
                    val word = list[i].toWord()
                    action.runInBackground(code, word)
                    add(code, word)
                }
            }
        }
        return this
    }

    fun clone(filter: (Map.Entry<Code, CodeItem>) -> CodeItem?): CodeTable {
        val table = CodeTable()
        forEach {
            filter(it)?.also { item ->
                table.addCodeItem(item)
            }
        }
        return table
    }
}

fun CodeTable(
    file: File,
    mutableMap: MutableMap<Code, CodeItem> = HashMap(),
    action: ProcessAction? = null,
): CodeTable {
    return CodeTable(mutableMap).apply { loadFromFile(file, action) }
}